Software Design Patterns
Florian Rappl, Fakultät für Physik, Universität Regensburg
Software Design Patterns
Introduction to modern software architecture
Clean code
Introduction
- Clean code has been made popular by Robert C. Martin
- In fact clean code tries to use a wide range of techniques to ensure
- stable and robust programs
- a short development time for extensions
- fewer bugs and less maintenance cycles
- We already learned a lot of techniques that are used with clean code
- Clean code is also driven by coding conventions and custom conventions
Grades
- The Clean Code Developer (CCD) initiative tries to embody all concepts
- The grade of a developer represents his skill / pursuit of these rules
- In total there are six grades:
- red
- orange
- yellow
- green
- blue
- white
Red
- Don't Repeat Yourself (DRY)
- Keep It Simple, Stupid (KISS)
- Favor Composition over Inheritance (FCoI)
- Using a version control system (VCS, e.g. git)
- Be cautious with optimizations
- Perform extensive root cause analysis
- Apply simple code refactoring methods
Orange
- Use a Single Level of Abstraction (SLA)
- Follow the SRP (see SOLID principles)
- Define and apply source code conventions
- Setup automatic integration tests
- Create a system for tracking issues
- Structure by Separation Of Concerns (SOC)
- Actively participate in code reviews and read books
Yellow
- Use three more SOLID principles:
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
- Liskov Substitution Principle (LSP)
- Perform automatic unit tests together with a code coverage analysis
- Create mockups for advanced testing
- Follow the Information Hiding Principle (IHP)
Green
- Open Close Principle (OCP) (now SOLID is fully applied)
- Use a continuous integration system
- Follow the law of demeter
- Integrate an Inversion of Control (IoC) container
- Evaluate code metrics
- Let classes follow the tell, don't ask principle
Blue
- Continuous delivery in place
- Test-driven development by test first
- Iterative development (small, very robust, yet extensible components)
- You Ain't Gonna Need It (YAGNI)
- Structuring applications in pieces, so called modules
- A class is then a piece of a module
- This modularity encourages parallel development
White
Information hiding
- Sometimes called encapsulation (controversial)
- The storage layout should be hidden and cannot be accessed outside
- Main advantage: Changing the layout will not change anything else
- Any kind of method that is implementation specific should be hidden
- Encapsulation is also a technique for providing a stable interface between multiple systems
- Some people consider inheritance to break encapsulation
Composition over inheritance
- Sometimes called Composite Reuse Principle and describes a technique for achieving polymorphic behavior
- Classes should references to other classes, that implement the desired functionality
- Therefore any functionality is outsourced and SRP is easily possible
- The ultimate goal is higher flexibility
- In other words, has a can be better than an is a relationship
- However, all methods need to be specified, not just a subset
Example
class GameObject
{
readonly IVisible _v;
readonly IUpdatable _u;
readonly ICollidable _c;
public GameObject(IVisible v, IUpdatable u, ICollidable c)
{
_v = v;
_u = u;
_c = c;
}
public void Update()
{
_u.Update();
}
public void Draw()
{
_v.Draw();
}
public void Collide()
{
_c.Collide();
}
}
interface IVisible
{
void Draw();
}
class Invisible : IVisible
{
public void Draw()
{
}
}
class Visible : IVisible
{
public void Draw()
{
/* draw model */
}
}
interface ICollidable
{
void Collide();
}
class Solid : ICollidable
{
public void Collide()
{
/* check collisions with object and react */
}
}
class NotSolid : ICollidable
{
public void Collide()
{
}
}
interface IUpdatable
{
void Update();
}
class Movable : IUpdatable
{
public void Update()
{
/* move object */
}
}
class NotMovable : IUpdatable
{
public void Update()
{
}
}
class Player : GameObject
{
public Player() : base(new Visible(), new Movable(), new Solid())
{
}
}
class Smoke : GameObject
{
public Smoke() : base(new Visible(), new Movable(), new NotSolid())
{
}
}
class GameObject {
private:
VisibilityDelegate* _v;
UpdateDelegate* _u;
CollisionDelegate* _c;
public:
GameObject(VisibilityDelegate* v, UpdateDelegate* u, CollisionDelegate* c) : _v(v), _u(u), _c(c) {
}
void Update() {
_u->Update();
}
void Draw() {
_v->Draw();
}
void Collide(GameObject objects[]) {
_c->Collide(objects);
}
};
class VisibilityDelegate {
public:
virtual void Draw() = 0;
};
class Invisible : public VisibilityDelegate {
public:
virtual void Draw() {
}
};
class Visible: public VisibilityDelegate {
public:
virtual void Draw() {
/* draw model */
}
};
class CollisionDelegate {
public:
virtual void Collide(GameObject objects[]) = 0;
};
class Solid : public CollisionDelegate {
public:
virtual void Collide(GameObject objects[]) {
/* check collisions with object and react */
}
};
class NotSolid : public CollisionDelegate {
public:
virtual void Collide(GameObject objects[]) {
}
};
class UpdateDelegate {
public:
virtual void Update() = 0;
};
class Movable : public UpdateDelegate {
public:
virtual void Update() {
/* move object */
}
};
class NotMovable : public UpdateDelegate {
public:
virtual void Update() {
}
};
class Player : public GameObject {
public:
Player() : GameObject(new Visible(), new Movable(), new Solid()) {
}
};
class Smoke : public GameObject {
public:
Smoke() : GameObject(new Visible(), new Movable(), new NotSolid()) {
}
};
Law of Demeter
- A specific case of loose coupling (design guideline)
- Each class should have only limited knowledge about other classes
- Only closely related classes should be known
- No communication with unrelated classes
- More strictly a class should never call the method of an object contained in another class, i.e.
a.b.c()
is not allowed, buta.b()
is - In general a method can only call methods from the current class, one of the parameters or a method of a global variable (application or class)
Single Level of Abstraction
- Readability is improved by clear structures (e.g. formatting)
- An important detail is the level of abstraction of a block
- The class name is one level, the public methods another
- It is important to never mix levels of abstraction
- Example: Bit-wise operations should not be mixed with method calls
- Reason: Reader decides how deep to dive into the code
Code coverage
- Code coverage describes how many possible paths are tested
- There are several kinds of coverages:
- Function coverage (is the function tested?)
- Statement coverage (is the statement called?)
- Condition coverage (is any possible value included?)
- State coverage (have all possible states been reached?)
- A high indicator (higher 80%) is usually a sign for a well-tested code
Code metrics
- Measuring the quality of software is usually highly subjective
- Code metrics give numbers that can be used to estimate the quality
- Maintainability index (0 to 100, high is better)
- Cyclomatic complexity (number of different paths)
- Depth of inheritance (lower is better)
- Class coupling (lower is better)
- Lines of code (or number of instructions)
- Cyclomatic complexity is the most important measurement
IoC container
- Task: Assembling components from different projects into one app
- Problem: How to do the wiring? Loose coupling?!
- Of course inversion of control has to be used (from the DIP) with factory
- Two choices for such a factory (called container):
- Service Locator (SL)
- Dependency Injection (DI)
- The SL has to be called explicitly, DI is an implicit construction (target classes don't need to know how the dependency will be resolved)
Continuous delivery
Literature
- Shalloway, Alan; Trott, James (2002). Design Patterns Explained.
- Humble, Jez; Farley, David (2010). Continuous delivery : reliable software releases through build, test, and deployment automation.
- Martin, Robert (2008). Clean Code: A Handbook of Agile Software Craftsmanship.
- Evans, Eric (2003). Domain-Driven Design: Tackling Complexity in the Heart of Software.
- Beck, Kent (2002). Test Driven Development. By Example.